package com.ai.aicodemather.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.ai.aicodemather.constant.AppConstant;
import com.ai.aicodemather.constant.UserConstant;
import com.ai.aicodemather.core.AiCodeGeneratorFacade;
import com.ai.aicodemather.exception.BusinessException;
import com.ai.aicodemather.exception.ErrorCode;
import com.ai.aicodemather.exception.ThrowUtils;
import com.ai.aicodemather.mapper.AppMapper;
import com.ai.aicodemather.model.dto.AppAddRequest;
import com.ai.aicodemather.model.dto.AppAdminUpdateRequest;
import com.ai.aicodemather.model.dto.AppQueryRequest;
import com.ai.aicodemather.model.dto.AppUpdateRequest;
import com.ai.aicodemather.model.entity.App;
import com.ai.aicodemather.model.entity.User;
import com.ai.aicodemather.model.enums.CodeGenTypeEnum;
import com.ai.aicodemather.model.vo.AppVO;
import com.ai.aicodemather.model.vo.UserVO;
import com.ai.aicodemather.service.AppService;
import com.ai.aicodemather.service.UserService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

import java.io.File;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 服务层实现。
 *
 * @author aiCodeMather
 * @since 2023-03-30
 */
@Service
public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService {

    @Resource
    private UserService userService;

    @Resource
    private AiCodeGeneratorFacade aiCodeGeneratorFacade;

    /**
     * 创建应用
     *
     * @param appAddRequest 应用添加请求
     * @param request       HTTP请求
     * @return 应用ID
     */
    @Override
    public Long createApp(AppAddRequest appAddRequest, HttpServletRequest request) {
        // 1. 参数校验
        if (appAddRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空！");
        }
        if (StrUtil.isBlank(appAddRequest.getInitPrompt())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "初始化提示词不能为空！");
        }

        // 2. 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }

        // 3. 保存应用信息
        App app = new App();
        BeanUtil.copyProperties(appAddRequest, app);
        app.setUserId(loginUser.getId());
        app.setAppName(appAddRequest.getInitPrompt().substring(0, Math.min(appAddRequest.getInitPrompt().length(), 12)));
        app.setCodeGenType(CodeGenTypeEnum.MULTI_FILE.getValue());

        boolean result = this.save(app);
        ThrowUtils.throwIf(!result, ErrorCode.SYSTEM_ERROR, "创建应用失败！");
        return app.getId();
    }

    /**
     * 根据 id 修改自己的应用
     *
     * @param appUpdateRequest 应用更新请求
     * @param request          HTTP请求
     * @return 是否成功
     */
    @Override
    public Boolean updateMyApp(AppUpdateRequest appUpdateRequest, HttpServletRequest request) {
        // 1. 参数校验
        if (appUpdateRequest == null || appUpdateRequest.getId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空！");
        }

        // 2. 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }

        // 3. 检查应用是否存在且属于当前用户
        App oldApp = this.getById(appUpdateRequest.getId());
        if (oldApp == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "应用不存在！");
        }
        if (!oldApp.getUserId().equals(loginUser.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限修改此应用！");
        }

        // 4. 更新应用信息
        App app = new App();
        BeanUtil.copyProperties(appUpdateRequest, app);
        app.setEditTime(LocalDateTime.now());

        boolean result = this.updateById(app);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新应用失败！");
        }
        return true;
    }

    /**
     * 根据 id 删除自己的应用
     *
     * @param id      应用ID
     * @param request HTTP请求
     * @return 是否成功
     */
    @Override
    public Boolean deleteMyApp(Long id, HttpServletRequest request) {
        // 1. 参数校验
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用ID不能为空！");
        }

        // 2. 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }

        // 3. 检查应用是否存在且属于当前用户
        App app = this.getById(id);
        if (app == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "应用不存在！");
        }
        // 仅本人和管理员可以删除用户
        if (!app.getUserId().equals(loginUser.getId()) && !UserConstant.ADMIN_ROLE.equals(loginUser.getUserRole())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限删除此应用！");
        }

        // 4. 删除应用
        boolean result = this.removeById(id);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除应用失败！");
        }
        return true;
    }

    /**
     * 根据 id 查看应用详情
     *
     * @param id 应用ID
     * @return 应用详情
     */
    @Override
    public AppVO getAppVOById(Long id) {
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用ID不能为空！");
        }
        App app = this.getById(id);
        if (app == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "应用不存在！");
        }
        return this.getAppVO(app);
    }

    /**
     * 分页查询自己的应用列表
     *
     * @param appQueryRequest 查询请求
     * @param request         HTTP请求
     * @return 应用列表
     */
    @Override
    public Page<AppVO> listMyAppByPage(AppQueryRequest appQueryRequest, HttpServletRequest request) {
        // 1. 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }

        // 2. 设置查询条件
        long pageNum = appQueryRequest.getPageNum();
        long pageSize = appQueryRequest.getPageSize();
        ThrowUtils.throwIf(pageSize > 20, ErrorCode.PARAMS_ERROR, "每页最多20个！");
        appQueryRequest.setUserId(loginUser.getId()); // 只查询自己的应用

        // 3. 查询数据
        Page<App> appPage = this.page(Page.of(pageNum, pageSize),
                this.getQueryWrapper(appQueryRequest));

        // 4. 转换为VO
        Page<AppVO> appVOPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = this.getAppVOList(appPage.getRecords());
        appVOPage.setRecords(appVOList);
        return appVOPage;
    }

    /**
     * 分页查询精选的应用列表
     *
     * @param appQueryRequest 查询请求
     * @return 应用列表
     */
    @Override
    public Page<AppVO> listFeaturedAppByPage(AppQueryRequest appQueryRequest) {
        long pageNum = appQueryRequest.getPageNum();
        long pageSize = appQueryRequest.getPageSize();
        ThrowUtils.throwIf(pageSize > 20, ErrorCode.PARAMS_ERROR, "每页最多20个！");

        // 查询精选应用
        appQueryRequest.setPriority(AppConstant.GOOD_APP_PRIORITY);
        QueryWrapper queryWrapper = this.getQueryWrapper(appQueryRequest);
        Page<App> appPage = this.page(Page.of(pageNum, pageSize), queryWrapper);

        // 转换为VO
        Page<AppVO> appVOPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = this.getAppVOList(appPage.getRecords());
        appVOPage.setRecords(appVOList);
        return appVOPage;
    }

    /**
     * 管理员根据 id 删除任意应用
     *
     * @param id 应用ID
     * @return 是否成功
     */
    @Override
    public Boolean deleteApp(Long id) {
        if (id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "应用ID不能为空！");
        }
        App app = this.getById(id);
        if (app == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "应用不存在！");
        }
        boolean result = this.removeById(id);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除应用失败！");
        }
        return true;
    }

    /**
     * 管理员根据 id 更新任意应用
     *
     * @param appUpdateRequest 应用更新请求
     * @return 是否成功
     */
    @Override
    public Boolean updateApp(AppAdminUpdateRequest appUpdateRequest) {
        if (appUpdateRequest == null || appUpdateRequest.getId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空！");
        }
        App oldApp = this.getById(appUpdateRequest.getId());
        if (oldApp == null) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "应用不存在！");
        }

        App app = new App();
        BeanUtil.copyProperties(appUpdateRequest, app);
        app.setEditTime(LocalDateTime.now());

        boolean result = this.updateById(app);
        if (!result) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新应用失败！");
        }
        return true;
    }

    /**
     * 管理员分页查询应用列表
     *
     * @param appQueryRequest 查询请求
     * @return 应用列表
     */
    @Override
    public Page<AppVO> listAppByPage(AppQueryRequest appQueryRequest) {
        long pageNum = appQueryRequest.getPageNum();
        long pageSize = appQueryRequest.getPageSize();

        Page<App> appPage = this.page(Page.of(pageNum, pageSize),
                this.getQueryWrapper(appQueryRequest));

        // 转换为VO
        Page<AppVO> appVOPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        List<AppVO> appVOList = this.getAppVOList(appPage.getRecords());
        appVOPage.setRecords(appVOList);
        return appVOPage;
    }

    /**
     * 获取应用VO
     *
     * @param app 应用实体
     * @return 应用VO
     */
    @Override
    public AppVO getAppVO(App app) {
        if (app == null) {
            return null;
        }
        AppVO appVO = new AppVO();
        BeanUtil.copyProperties(app, appVO);
        Long userId = app.getUserId();
        if (userId != null) {
            User user = userService.getById(app.getUserId());
            appVO.setUser(userService.getUserVO(user));
        }
        return appVO;
    }

    /**
     * 获取应用VO列表
     *
     * @param appList 应用实体列表
     * @return 应用VO列表
     */
    @Override
    public List<AppVO> getAppVOList(List<App> appList) {
        if (CollUtil.isEmpty(appList)) {
            return new ArrayList<>();
        }
        // 批量获取user
        List<Long> userIdList = appList.stream()
                .map(App::getUserId)
                .collect(Collectors.toList());
        // 批量查询user并转换为userVo，并赋值给appVO
        List<User> userList = userService.listByIds(userIdList);
        ThrowUtils.throwIf(userList.size() != userIdList.size(), ErrorCode.SYSTEM_ERROR, "用户信息查询失败");
        Map<Long, UserVO> userMap = userList.stream()
                .collect(Collectors.toMap(User::getId, userService::getUserVO));
        return appList.stream().
                map(app -> {
                    AppVO appVO = this.getAppVO(app);
                    appVO.setUser(userMap.get(app.getUserId()));
                    return appVO;
                })
                .collect(Collectors.toList());
    }

    /**
     * 聊天生成代码，流式
     *
     * @param appId
     * @param userMessage
     * @param request
     * @return
     */
    @Override
    public Flux<String> chatToGenCode(Long appId, String userMessage, HttpServletRequest request) {
        // 1. 参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用id为空");
        ThrowUtils.throwIf(StrUtil.isBlank(userMessage), ErrorCode.PARAMS_ERROR, "用户提示词为空");
        // 2. 查询应用信息
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        // 3. 权限校验，仅限和自己对话
        User loginUser = userService.getLoginUser(request);
        ThrowUtils.throwIf(!app.getUserId().equals(loginUser.getId()), ErrorCode.NO_AUTH_ERROR, "无权限操作");
        // 4. 获取代码生成类型
        CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(app.getCodeGenType());
        ThrowUtils.throwIf(codeGenTypeEnum == null, ErrorCode.SYSTEM_ERROR, "不支持的代码类型: " + app.getCodeGenType());
        // 5. 调用ai生成代码
        return aiCodeGeneratorFacade.generateAndSaveStreamCode(userMessage, codeGenTypeEnum, appId);
    }

    /**
     * 部署应用
     *
     * @param appId
     * @param user
     * @return
     */
    @Override
    public String deployApp(Long appId, User user) {
        // 1. 参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用id为空");
        ThrowUtils.throwIf(user == null, ErrorCode.NOT_LOGIN_ERROR, "用户未登录");
        // 2. 获取应用信息
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR, "应用不存在");
        // 3. 权限校验
        ThrowUtils.throwIf(!app.getUserId().equals(user.getId()), ErrorCode.NO_AUTH_ERROR, "无权限操作");
        // 4. 部署key是否存在，不存在的话创建一个，保证唯一
        String deployKey = app.getDeployKey();
        if (StrUtil.isBlank(deployKey)) {
            deployKey = RandomUtil.randomString(6);
        }
        // 5. 获取代码生成类型, 构建源代码路径
        String codeGenType = app.getCodeGenType();
        ThrowUtils.throwIf(StrUtil.isBlank(codeGenType), ErrorCode.SYSTEM_ERROR, "代码生成类型为空");
        String sourceFileName = String.format("%s_%s", codeGenType, appId);
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceFileName;

        // 6. 源文件是否存在
        File sourceFile = new File(sourceDirPath);
        if (!sourceFile.exists() || !sourceFile.isDirectory()) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "代码文件不存在，请先生成代码！");
        }
        // 7. 将文件从output目录复制到部署deploy目录
        String deployDirPath = AppConstant.CODE_DEPLOY_ROOT_DIR + File.separator + deployKey;
        try {
            FileUtil.copyContent(sourceFile, new File(deployDirPath), true);
        } catch (Exception e) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "部署失败：" + e.getMessage());
        }
        // 8. 更新应用到的部署key和部署时间
        App updateApp = new App();
        updateApp.setId(appId);
        updateApp.setDeployKey(deployKey);
        updateApp.setDeployedTime(LocalDateTime.now());
        boolean updateById = this.updateById(updateApp);
        ThrowUtils.throwIf(!updateById, ErrorCode.SYSTEM_ERROR, "更新应用部署信息失败");
        // 9. 返回可查看的url
        return String.format("%s/%s/", AppConstant.CODE_DEPLOY_HOST, deployKey);
    }

    /**
     * 获取查询包装器
     *
     * @param appQueryRequest 查询请求
     * @return 查询包装器
     */
    @Override
    public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
        if (appQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = appQueryRequest.getId();
        String appName = appQueryRequest.getAppName();
        String cover = appQueryRequest.getCover();
        String codeGenType = appQueryRequest.getCodeGenType();
        String deployKey = appQueryRequest.getDeployKey();
        Integer priority = appQueryRequest.getPriority();
        Long userId = appQueryRequest.getUserId();
        String sortField = appQueryRequest.getSortField();
        String sortOrder = appQueryRequest.getSortOrder();

        return QueryWrapper.create()
                .eq("id", id)
                .eq("userId", userId)
                .eq("priority", priority)
                .like("appName", appName)
                .like("cover", cover)
                .like("codeGenType", codeGenType)
                .like("deployKey", deployKey)
                .eq("isDelete", 0)
                .orderBy(sortField, "ascend".equals(sortOrder));
    }
}
